#!/usr/bin/python3
# -*- coding: utf-8 -*-

"""
# @version: v1.0
# @author : wlisingle
# @Email : 19688513@qq.com
# @Project : g-carbon-bio
# @File : stock_pooled_bidding_strategy.py
# @Software: PyCharm
# @time: 2025/2/25 10:33
# @description : 当天集合竞价9：15分至9：31分.成交金额小于上个交易日千分之十，
# 当天下跌前2个交易日下跌;
# 不要ST股及不要退市股;不要科创板;不要北交所，换手率低于上个交易日
"""
import csv

from service.models.stock_analysis_result import StockAnalysisResult
from service.models.stock_sh import StockSh
from service.models.stock_sz import StockSz
from service.stocksh_service import StockShService
from service.stocksz_service import StockSzService
from datetime import datetime, timedelta
import logging

from service.trade_date_calendar import TradeDateCalendar
from strategies.historical_stockdata_fetcher import HistoricalStockDataFetcher

# 全局变量控制打印
DEBUG = True

class StockPooledBiddingStrategy:
    def __init__(self):
        """
        股票集合竞价策略类的初始化
        """
        self.stock_sh_service = StockShService()  # 创建上海股票服务实例
        self.stock_sz_service = StockSzService()  # 创建深圳股票服务实例
        self.stock_historical = HistoricalStockDataFetcher()
        self.trade_calendar = TradeDateCalendar()

    def get_pooled_bidding_data(self, stock_service, table_name):
        """
        获取并处理股票数据
        """
        output_lines = []  # 存储结果的列表

        try:
            if DEBUG:
                print(f"开始处理 {table_name} 数据...")

            # 获取独特的日期
            dates = stock_service.get_distinct_dates_am()
            if not dates:
                logging.warning("未找到任何交易日期。")
                return output_lines

            if DEBUG:
                print(f"获取到 {len(dates)} 个交易日期。")

            # 确保 dates 是 datetime.date 类型
            if isinstance(dates[0], str):
                dates = [datetime.strptime(date, "%Y-%m-%d").date() for date in dates]

            min_date = min(dates)  # 获取最小日期

            for i in range(len(dates)):
                current_date = dates[i]

                if i + 3 >= len(dates):
                    continue

                three_days_prior = dates[i + 3]
                if three_days_prior < min_date:
                    continue

                current_date_plus_one = current_date + timedelta(days=1)
                conditions = [f"creation_time BETWEEN '{three_days_prior} %' AND '{current_date_plus_one} %'"]

                if DEBUG:
                    print(f"处理日期: {current_date}, 条件: {conditions}")

                # 获取起始和结束日期
                previous_start_date, next_day = self.trade_calendar.get_start_end_date(
                    current_date.strftime("%Y-%m-%d"), 120)

                if DEBUG:
                    print(f"获取历史数据: 起始日期 {previous_start_date}, 结束日期 {next_day}")

                # 获取股票数据
                stocks = stock_service.get_where(table_name, StockSh, conditions)

                if DEBUG:
                    print(f"获取到 {len(stocks)} 条股票数据。")

                # 筛选出 change_percent 为负数的股票
                negative_change_stocks = [stock for stock in stocks if stock.change_percent < 0]

                if DEBUG:
                    print(f"筛选出 {len(negative_change_stocks)} 条下跌股票数据。")

                # 分组股票
                grouped_stocks = {}
                for stock in negative_change_stocks:
                    if stock.code in grouped_stocks:
                        grouped_stocks[stock.code].append(stock)
                    else:
                        grouped_stocks[stock.code] = [stock]

                for code, stocks in grouped_stocks.items():
                    # 跳过以688开头的股票代码
                    if code.startswith("688"):
                        continue

                    if len(stocks) == 3:
                        historical_data_list = self.stock_historical.fetch_stock_data(code, previous_start_date,
                                                                                      next_day)
                        close_prices = [hd.close_price for hd in historical_data_list if hd.close_price is not None]

                        if DEBUG:
                            print(f"获取到股票代码 {code} 的历史数据，共 {len(close_prices)} 条收盘价数据。")

                        # 计算移动平均线，仅包括有效的结果
                        ma_list = []
                        for window in [5, 10, 20, 30, 60, 120]:
                            ma = self.calculate_moving_average(close_prices, window)
                            if ma is not None:
                                ma_list.append(round(ma, 2))

                        amounts = []
                        for stock in stocks:
                            total_amount = stock.buy_amount + stock.sell_amount + stock.neutral_amount
                            creation_time = datetime.strptime(stock.creation_time, '%Y-%m-%d %I:%M:%S %p')
                            amounts.append((creation_time, total_amount))

                        amounts.sort(key=lambda x: x[0], reverse=True)

                        if len(amounts) < 3:
                            continue

                        first_date, first_amount = amounts[0]
                        second_date, second_amount = amounts[1]
                        third_date, third_amount = amounts[2]

                        second_ratio = round(second_amount / first_amount, 2) if first_amount != 0 else 0
                        third_ratio = round(third_amount / second_amount, 2) if second_amount != 0 else 0

                        # 获取下一天的日期
                        next_day_start, next_day = self.trade_calendar.get_offset_trade_dates(
                            current_date.strftime("%Y-%m-%d"), 1)
                        next_day_historical_data = self.stock_historical.fetch_stock_data(code, next_day_start,
                                                                                          next_day)

                        next_day_datetime = datetime.strptime(next_day, "%Y%m%d")  # 假设 next_day 是 "YYYYMMDD" 格式的字符串

                        next_day_close = None
                        previous_day_close = None

                        for hd in next_day_historical_data:
                            if hd.date == datetime.strptime(next_day_start, "%Y%m%d").date():
                                next_day_close = hd.close_price
                            if hd.date == next_day_datetime.date():
                                previous_day_close = hd.close_price

                        next_day_change_percent = (
                            round(((next_day_close - previous_day_close) / previous_day_close) * 100, 2)
                            if next_day_close is not None and previous_day_close is not None else None
                        )

                        # 获取最新价格（latest_price）
                        latest_price = next_day_close  # 这里可以根据实际情况调整

                        stock_result = StockAnalysisResult(
                            first_date, first_amount, second_date, second_amount,
                            third_date, third_amount, code, second_ratio,
                            third_ratio, next_day_date=next_day_datetime,
                            next_day_change_percent=next_day_change_percent,
                            ma5=ma_list[0] if len(ma_list) > 0 else None,
                            ma10=ma_list[1] if len(ma_list) > 1 else None,
                            ma20=ma_list[2] if len(ma_list) > 2 else None,
                            ma30=ma_list[3] if len(ma_list) > 3 else None,
                            ma60=ma_list[4] if len(ma_list) > 4 else None,
                            ma120=ma_list[5] if len(ma_list) > 5 else None,
                            latest_price=latest_price
                        )

                        output_lines.append(stock_result)

                        if DEBUG:
                            print(f"处理股票代码 {code} 完成，结果已添加到输出列表。")

            # 根据 next_day 进行排序
            output_lines.sort(key=lambda x: x.next_day_date, reverse=True)

            if DEBUG:
                print(f"处理完成，共生成 {len(output_lines)} 条结果。")

        except Exception as e:
            logging.exception("获取股票数据时发生错误: %s", e)

        return output_lines

    def calculate_moving_average(self, prices, window):
        """
        计算移动平均线
        :param prices: 收盘价列表
        :param window: 滚动窗口大小
        :return: 移动平均值
        """
        if len(prices) < window:
            return None  # 如果数据不足，返回 None

        return sum(prices[-window:]) / window  # 计算最后 `window` 个价格的平均值

    def get_pooled_bidding_date(self):
        """
        获取上海和深圳的股票数据并保存到 CSV 文件
        """
        try:
            if DEBUG:
                print("开始获取上海和深圳的股票数据...")

            # 获取上海和深圳的股票数据
            sh_output = self.get_pooled_bidding_data(self.stock_sh_service, 'stocksh_am')
            sz_output = self.get_pooled_bidding_data(self.stock_sz_service, 'stocksz_am')

            # 在上海股票代码后加上 .SH
            for stock_result in sh_output:
                stock_result.code += ".SH"

            # 在深圳股票代码后加上 .SZ
            for stock_result in sz_output:
                stock_result.code += ".SZ"

            # 合并输出结果
            output_lines = sh_output + sz_output

            if DEBUG:
                print(f"合并后的结果共 {len(output_lines)} 条。")

            # 将所有结果写入 CSV 文件
            with open('output.csv', 'w', newline='', encoding='utf-8') as f:
                writer = csv.writer(f)
                writer.writerow([
                    '代码', '第二量比', '第三量比',
                    '次日日期', '次日涨跌幅',
                    '最新价',  # 新增最新价
                    '5日均线', '是否突破5日均线',  # 新增是否突破5日均线
                    '10日均线', '是否突破10日均线',  # 新增是否突破10日均线
                    '20日均线', '是否突破20日均线',  # 新增是否突破20日均线
                    '30日均线', '是否突破30日均线',  # 新增是否突破30日均线
                    '60日均线', '是否突破60日均线',  # 新增是否突破60日均线
                    '120日均线', '是否突破120日均线',  # 新增是否突破120日均线

                    '第一日期', '第一数量',
                    '第二日期', '第二数量',
                    '第三日期', '第三数量',
                ])
                for stock_result in output_lines:
                    writer.writerow(stock_result.to_list())  # 写入每个对象的数据

            if DEBUG:
                print("股票数据处理完成，结果已保存到 output.csv 文件中。")

        except Exception as e:
            logging.exception("保存股票数据时发生错误: %s", e)


# 示例使用
if __name__ == "__main__":
    strategy = StockPooledBiddingStrategy()
    strategy.get_pooled_bidding_date()  # 执行数据处理
    print("股票数据处理完成，结果已保存到 output.csv 文件中。")